package com.ccrfid.rmc6.service.business.prison.door;

import com.ccrfid.rmc6.appconfig.exception.pojo.AppException;
import com.ccrfid.rmc6.appconfig.exception.pojo.CustomErrors;
import com.ccrfid.rmc6.entity.business.prison.door.InOutDoorEntity;
import com.ccrfid.rmc6.entity.business.prison.door.InOutRecordDetailEntity;
import com.ccrfid.rmc6.entity.business.prison.door.InOutRecordEntity;
import com.ccrfid.rmc6.entity.personnel.PersonnelEntity;
import com.ccrfid.rmc6.entity.sys.OrgEntity;
import com.ccrfid.rmc6.pojo.dto.prison.door.CountDto;
import com.ccrfid.rmc6.pojo.dto.prison.door.InOutCountDto;
import com.ccrfid.rmc6.pojo.dto.prison.door.InOutRecordDto;
import com.ccrfid.rmc6.repository.business.prison.door.InOutDoorRepository;
import com.ccrfid.rmc6.repository.business.prison.door.InOutRecordDetailRepository;
import com.ccrfid.rmc6.repository.business.prison.door.InOutRecordRepository;
import com.ccrfid.rmc6.repository.personnel.PersonnelRepository;
import com.ccrfid.rmc6.repository.search.SearchCriteria;
import com.ccrfid.rmc6.service.sys.OrgService;
import com.ccrfid.rmc6.util.DateUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.transaction.Transactional;
import javax.ws.rs.core.Response;
import java.sql.Timestamp;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;

@Service
@Transactional
public class DoorServiceImpl implements DoorService {
    private static final Logger logger = LoggerFactory.getLogger(DoorServiceImpl.class);
    @PersistenceContext
    private EntityManager entityManager;
    @Autowired
    private OrgService orgService;
    @Autowired
    private InOutDoorRepository inOutDoorRepository;
    @Autowired
    private InOutRecordRepository inOutRecordRepository;
    @Autowired
    private InOutRecordDetailRepository inOutRecordDetailRepository;
    @Autowired
    private PersonnelRepository personnelRepository;

    @Override
    public List<InOutDoorEntity> getAllDoors() {
        return inOutDoorRepository.getAllBy();
    }

    @Override
    public InOutDoorEntity getDoorById(Long doorId) {
        return inOutDoorRepository.getById(doorId);
    }

    private void checkDoorEntity(InOutDoorEntity doorEntity) throws AppException {
        if (doorEntity.getInLocatorAddress() == null) {
            throw new AppException(Response.Status.BAD_REQUEST.getStatusCode(),
                    CustomErrors.door_in_locator_is_null.getCode(), CustomErrors.door_in_locator_is_null.getReason(), "");
        }
        if (doorEntity.getOutLocatorAddress() == null) {
            throw new AppException(Response.Status.BAD_REQUEST.getStatusCode(),
                    CustomErrors.door_out_locator_is_null.getCode(), CustomErrors.door_out_locator_is_null.getReason(), "");
        }
        if (doorEntity.getInLocatorAddress().equals(doorEntity.getOutLocatorAddress())) {
            throw new AppException(Response.Status.BAD_REQUEST.getStatusCode(),
                    CustomErrors.door_in_out_locator_conflict.getCode(), CustomErrors.door_in_out_locator_conflict.getReason(), "");
        }
    }

    @Override
    public InOutDoorEntity createDoor(InOutDoorEntity doorEntity) throws AppException {
        checkDoorEntity(doorEntity);
        return inOutDoorRepository.save(doorEntity);
    }

    @Override
    public InOutDoorEntity updateDoor(Long doorId, InOutDoorEntity doorEntity) throws AppException {
        checkDoorEntity(doorEntity);
        doorEntity.setId(doorId);
        return inOutDoorRepository.save(doorEntity);
    }

    @Override
    public void deleteDoorById(Long doorId) throws AppException {
        if (!inOutDoorRepository.existsById(doorId)) {
            throw new AppException(Response.Status.BAD_REQUEST.getStatusCode(),
                    CustomErrors.resource_not_found.getCode(), CustomErrors.resource_not_found.getReason(), "");
        } else {
            if (inOutRecordRepository.existsByDoorId(doorId)) {
                List<InOutRecordEntity> recordEntityList = inOutRecordRepository.getAllByDoorId(doorId);
                for (InOutRecordEntity recordEntity : recordEntityList) {
                    if (inOutRecordDetailRepository.existsByRecordId(recordEntity.getId())) {
                        inOutRecordDetailRepository.deleteAllByRecordId(recordEntity.getId());
                    }
                }
                inOutRecordRepository.deleteAllByDoorId(doorId);
            }
            inOutDoorRepository.deleteById(doorId);
        }
    }

    @Override
    public Page<InOutRecordEntity> getInOutRecords(SearchCriteria criteria, Pageable pageable) {
        return inOutRecordRepository.search(criteria, pageable);
    }

    @Override
    public Page<InOutRecordDetailEntity> getInOutRecordDetail(SearchCriteria criteria, Pageable pageable) {
        return inOutRecordDetailRepository.search(criteria, pageable);
    }

    @Override
    public InOutRecordEntity startInOutRecord(Long doorId, String type, Long policeId, String policeName, String remark)
            throws AppException {
        if (!inOutDoorRepository.existsById(doorId)) {
            throw new AppException(Response.Status.BAD_REQUEST.getStatusCode(),
                    CustomErrors.door_id_not_found.getCode(), CustomErrors.door_id_not_found.getReason(), "");
        }
        if (StringUtils.isEmpty(type)) {
            throw new AppException(Response.Status.BAD_REQUEST.getStatusCode(),
                    CustomErrors.in_out_type_is_null.getCode(), CustomErrors.in_out_type_is_null.getReason(), "");
        }
        InOutRecordEntity recordEntity = new InOutRecordEntity();
        recordEntity.setDoorId(doorId);
        recordEntity.setType(type);
        recordEntity.setPoliceId(policeId);
        recordEntity.setPoliceName(policeName);
        recordEntity.setStartTime(new Timestamp(Instant.now().toEpochMilli()));
        recordEntity.setRemark(remark);
        return inOutRecordRepository.save(recordEntity);
    }

    @Override
    public InOutRecordDetailEntity createInOutRecordDetail(Long recordId, InOutRecordDetailEntity recordDetailEntity)
            throws AppException {
        if (!inOutRecordRepository.existsById(recordId)) {
            throw new AppException(Response.Status.BAD_REQUEST.getStatusCode(),
                    CustomErrors.door_in_out_record_id_not_found.getCode(), CustomErrors.door_in_out_record_id_not_found.getReason(), "");
        }
        InOutRecordEntity recordEntity = inOutRecordRepository.getById(recordId);
        if (recordEntity.getStopTime() == null) {
            recordDetailEntity.setId(null);
            recordDetailEntity.setRecordId(recordId);
            if (recordDetailEntity.getRecordTime() == null) {
                recordDetailEntity.setRecordTime(new Timestamp(Instant.now().toEpochMilli()));
            }
            return inOutRecordDetailRepository.save(recordDetailEntity);
        } else {
            throw new AppException(Response.Status.BAD_REQUEST.getStatusCode(),
                    CustomErrors.door_in_out_record_error.getCode(), CustomErrors.door_in_out_record_error.getReason(), "");
        }
    }

    @Override
    public InOutRecordEntity stopInOutRecord(Long recordId) throws AppException {
        if (!inOutRecordRepository.existsById(recordId)) {
            throw new AppException(Response.Status.BAD_REQUEST.getStatusCode(),
                    CustomErrors.door_in_out_record_id_not_found.getCode(), CustomErrors.door_in_out_record_id_not_found.getReason(), "");
        }
        InOutRecordEntity recordEntity = inOutRecordRepository.getById(recordId);
        recordEntity.setStopTime(new Timestamp(Instant.now().toEpochMilli()));
        return inOutRecordRepository.save(recordEntity);
    }

    @Override
    public InOutCountDto getInOutCountByOrg(String dateStr) {
        InOutCountDto resultDto = new InOutCountDto();
        List<OrgEntity> orgList = orgService.getAllPrisonAreas();
        if (orgList != null && orgList.size() > 0) {
            List<CountDto> inList = new ArrayList<>();
            List<CountDto> outList = new ArrayList<>();
            for (OrgEntity orgEntity : orgList) {
                List<Long> subIdList = orgService.getSubOrgIdList(orgEntity.getId());
                inList.add(new CountDto(orgEntity.getOrgName(), subIdList));
                outList.add(new CountDto(orgEntity.getOrgName(), subIdList));
            }
            //
            List<PersonnelEntity> inPersonnelList = getInOutPersonnelList("in", "收工", dateStr);
            for (PersonnelEntity personnelEntity : inPersonnelList) {
                inList.stream().forEach(countDto -> {
                    if (countDto.containsOrg(personnelEntity.getOrgId())) {
                        countDto.countPlus();
                    }
                });
            }
            //
            List<PersonnelEntity> outPersonnelList = getInOutPersonnelList("out", "出工", dateStr);
            for (PersonnelEntity personnelEntity : outPersonnelList) {
                outList.stream().forEach(countDto -> {
                    if (countDto.containsOrg(personnelEntity.getOrgId())) {
                        countDto.countPlus();
                    }
                });
            }
            resultDto.setIn(inList);
            resultDto.setOut(outList);
        }
        return resultDto;
    }

    private List<PersonnelEntity> getInOutPersonnelList(String type, String remark, String dateStr) {
        String sql = "select * from personnel p where p.id in (" +
                " SELECT d.personnel_id from in_out_record_detail d " +
                " LEFT JOIN in_out_record r on d.record_id = r.id " +
                " where r.type = :type and r.remark = :remark and (d.record_time between :startTime and :stopTime))";
        List<PersonnelEntity> list = entityManager.createNativeQuery(sql, PersonnelEntity.class)
                .setParameter("type", type)
                .setParameter("remark", remark)
                .setParameter("startTime", DateUtil.parseDateTimeStr(dateStr.trim() + " 00:00:00"))
                .setParameter("stopTime", DateUtil.parseDateTimeStr(dateStr.trim() + " 23:59:59"))
                .getResultList();
        return list;
    }

    @Override
    public InOutRecordEntity takeInOut(Long doorId, String type, InOutRecordDto dto) throws AppException {
        //1
        InOutRecordEntity recordEntity = startInOutRecord(doorId, type, dto.getPoliceId(), dto.getPoliceName(), dto.getRemark());
        //2
        List<Long> personnelIdList = dto.getPersonnelIdList();
        if (personnelIdList != null && personnelIdList.size() > 0) {
            for (Long personnelId : personnelIdList) {
                PersonnelEntity personnelEntity = personnelRepository.getById(personnelId);
                if (personnelEntity != null) {
                    InOutRecordDetailEntity detailEntity = new InOutRecordDetailEntity();
                    detailEntity.setRecordId(recordEntity.getId());
                    detailEntity.setPersonnelId(personnelId);
                    detailEntity.setPersonnelName(personnelEntity.getPersonnelName());
                    detailEntity.setCardNumber(personnelEntity.getCardNumber());
                    detailEntity.setRecordTime(recordEntity.getStartTime());
                    inOutRecordDetailRepository.save(detailEntity);
                }
            }
        }
        //3
        recordEntity.setStopTime(new Timestamp(Instant.now().toEpochMilli()));
        return inOutRecordRepository.save(recordEntity);
    }
}
